package it.crosato.stage.server.controller;

import it.crosato.stage.server.model.actions.IActions;
import it.crosato.stage.shared.objects.Comparison;
import it.crosato.stage.shared.objects.EntityDefinition;
import it.crosato.stage.shared.objects.SelectionLists;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Vector;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MainController {
	
	@Autowired
	private IActions actions;
	
	/**
	 * Invocato alla richiesta della prima pagina
	 * @param request oggetto relativo alla richiesta
	 * @return l'oggetto relativo alla prima pagina
	 */
	@RequestMapping(value = "/index", method = { RequestMethod.GET })
	public ModelAndView welcome(HttpServletRequest request) {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("welcome");
		return mav;
	}
	
	/**
	 * Invocato alla richiesta della pagina di selezione dei dati. Si occupa di
	 * recuperare le liste di organismi e vie metaboliche disponibili mediante
	 * il riferimento actions
	 * @param request l'oggetto relativo alla richiesta
	 * @param time il tempo di scadenza delle informazioni della banca dati interna
	 * scelto dall'utente
	 * @return l'oggetto relativo alla pagina di selezione dei dati per il confronto
	 */
	@RequestMapping(value = "/selection", method = { RequestMethod.GET })
	public ModelAndView selection(HttpServletRequest request, @RequestParam("time") long time) {
		request.getSession().setAttribute("time", time);
		ModelAndView mav = new ModelAndView();
		mav.setViewName("selection");
		SelectionLists lists= actions.getLists(time);
		if (lists.getOrganisms() != null && lists.getPathways() != null) {
			mav.addObject("organisms", lists.getOrganisms());
			mav.addObject("pathways", lists.getPathways());
			mav.addObject("error", false);
		}
		else {
			mav.addObject("error", true);
		}
		return mav;
	}

	/**
	 * Invocato alla richiesta della pagina dei risultati. Effettua il confronto
	 * e memorizza i risultati in attributi di sessione. Utilizza il riferimento
	 * actions
	 * @param request l'oggetto relativo alla richiesta
	 * @param orgList lista degli organismi scelti in formato codice---descrizione
	 * @param mapList lista delle vie metaboliche scelte in formato codice---descrizione
	 * @param factor fattore di influenza in percentuali del metodo di rappresentazione
	 * basato sulle reti di Petri
	 * @param enzymesSet indica se considerare gli enzimi al posto delle reazioni
	 * nel confronto basato sugli insiemi
	 * @param enzymesInv indica se considerare gli enzimi al posto delle reazioni
	 * nel confronto basato sulle reti di Petri
	 * @return l'oggetto relativo alla pagina di visualizzazione delle vie metaboliche
	 */
	@RequestMapping(value = "/comparison", method = { RequestMethod.POST })
	public ModelAndView comparison(HttpServletRequest request, @RequestParam("orgList") String[] orgList,
			@RequestParam("mapList") String[] mapList, @RequestParam("factor") double factor,
			@RequestParam("reaEnzI") boolean enzymesSet, @RequestParam("reaEnzP") boolean enzymesInv) {
		
		Vector<EntityDefinition> selectedOrg = getSelectedDefinitions(orgList);
		Vector<EntityDefinition> selectedPat = getSelectedDefinitions(mapList);
		request.getSession().setAttribute("selectedOrganisms", selectedOrg);
		request.getSession().setAttribute("selectedPathways", selectedPat);
		request.getSession().setAttribute("enzS", enzymesSet);
		request.getSession().setAttribute("enzI", enzymesInv);
		request.getSession().setAttribute("factor", factor);
		long time = Long.class.cast(request.getSession().getAttribute("time"));
		String sessionId = request.getSession().getId();
		String applicationPath = request.getSession().getServletContext().getRealPath("/");
		Comparison comparison = actions.compare(selectedOrg, selectedPat, enzymesSet, enzymesInv,
				factor, time, sessionId, applicationPath);
		if (comparison.getPathways() == null) {
			ModelAndView mav = new ModelAndView();
			mav.setViewName("pathways");
			mav.addObject("error", true);
			return mav;
		}
		else {
			request.getSession().setAttribute("nonExistentPathways", comparison.getNonExistentPathways());
			request.getSession().setAttribute("pathways", comparison.getPathways());
			request.getSession().setAttribute("setDM", comparison.getSetDM());
			request.getSession().setAttribute("sets", comparison.getSets());
			request.getSession().setAttribute("invDM", comparison.getInvDM());
			request.getSession().setAttribute("invariants", comparison.getInvariants());
			request.getSession().setAttribute("matches", comparison.getMatches());
			request.getSession().setAttribute("totDM", comparison.getTotDM());
			request.getSession().setAttribute("upgma", comparison.getUpgma());
			request.getSession().setAttribute("nj", comparison.getNj());
			request.getSession().setAttribute("result", true);
			return pathways(request);
		}
	}
	
	/**
	 * Invocato alla rischiesta della pagina relativa ai dettagli delle vie metaboliche
	 * selezionate
	 * @param request l'oggetto relativo alla richiesta
	 * @return l'oggetto relativo alla pagina di visualizzazione delle vie metaboliche
	 */
	@RequestMapping(value = "/pathways", method = { RequestMethod.GET })
	public ModelAndView pathways(HttpServletRequest request) {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("pathways");
		mav.addObject("error", false);
		return mav;
	}
	
	/**
	 * Invcato alla richiesta della pagina di visualizzazione dei dettagli
	 * del confronto basato sugli insiemi.
	 * @param request l'oggetto relativo alla richiesta
	 * @return l'oggetto relativo alla pagina di visualizzazione dei dettagli 
	 * relativi al confronto basato sugli insiemi
	 */
	@RequestMapping(value = "/sets", method = { RequestMethod.GET })
	public ModelAndView sets(HttpServletRequest request) {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("sets");
		return mav;
	}
	
	/**
	 * Invcato alla richiesta della pagina di visualizzazione dei dettagli
	 * del confronto basato sulle reti di Petri.
	 * @param request l'oggetto relativo alla richiesta
	 * @return l'oggetto relativo alla pagina di visualizzazione dei dettagli 
	 * relativi al confronto basato sulle reti di Petri
	 */
	@RequestMapping(value = "/invariants", method = { RequestMethod.GET })
	public ModelAndView invariants() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("invariants");
		return mav;
	}
	
	/**
	 * Invocato alla richiesta della pagina di visualizzazione delle matrici di
	 * distanza
	 * @return l'oggetto relativo alla pagina delle matrici di distanza
	 */
	@RequestMapping(value = "/mapping", method = { RequestMethod.GET })
	public ModelAndView mapping() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("mapping");
		return mav;
	}
	
	/**
	 * Invocato alla richiesta della pagina per la visualizzazione degli
	 * alberi filogenetici
	 * @return l'oggetto relativo alla pagina degli alberi
	 */
	@RequestMapping(value = "/tree", method = { RequestMethod.GET })
	public ModelAndView tree() {
		ModelAndView mav = new ModelAndView();
		mav.setViewName("tree");
		return mav;
	}

	/**
	 * Invocato alla richiesta dell'immagine dell'alberdo filogenetico
	 * costruito mediante l'algoritmo UPGMA. Costruisce la risposta mediante
	 * l'oggetto che rappresenta l'immagine dell'albero
	 * @param request l'oggetto relativo alla richiesta
	 * @param response l'oggetto relativo alla risposta, sul quale insirire
	 * l'immagine
	 */
	@RequestMapping(value = "/upgma", method = { RequestMethod.GET})
	public void upgma(HttpServletRequest request, HttpServletResponse response) {
		BufferedImage upgma = BufferedImage.class.cast(request.getSession().getAttribute("upgma"));
		response.setContentType("image/png");
		try {
			ImageIO.write(upgma, "png", response.getOutputStream());
			response.getOutputStream().flush();
			response.getOutputStream().close();
		}
		catch (IOException ioe) {
			Logger.getLogger("myLog").info(ioe.getMessage());
		}
	}
	
	/**
	 * Invocato alla richiesta dell'immagine dell'alberdo filogenetico
	 * costruito mediante l'algoritmo Neighbour Joining. Costruisce la risposta mediante
	 * l'oggetto che rappresenta l'immagine dell'albero
	 * @param request l'oggetto relativo alla richiesta
	 * @param response l'oggetto relativo alla risposta, sul quale insirire
	 * l'immagine
	 */
	@RequestMapping(value = "/nj", method = { RequestMethod.GET})
	public void nj(HttpServletRequest request, HttpServletResponse response) {
		BufferedImage nj = BufferedImage.class.cast(request.getSession().getAttribute("nj"));
		response.setContentType("image/png");
		try {
			ImageIO.write(nj, "png", response.getOutputStream());
			response.getOutputStream().flush();
			response.getOutputStream().close();
		}
		catch (IOException ioe) {
			Logger.getLogger("myLog").info(ioe.getMessage());
		}
	}

	/**
	 * Trasforma le liste definizioni (organismi o vie) ricevute nel formato
	 * codice---descrizione in oggetti di tipo EntityDefinition 
	 * @param selected lista di definizioni nel formato codice---descrizione
	 * @return lista delle definizioni del tipo EntityDefinition
	 */
	private Vector<EntityDefinition> getSelectedDefinitions(String[] selected) {
		Vector<EntityDefinition> definitions = new Vector<EntityDefinition>();
		for (int i = 0; i < selected.length; i++) {
			String id = selected[i].split("---")[0];
			String description = selected[i].split("---")[1];
			definitions.add(new EntityDefinition(id, description));
		}
		return definitions;
	}
	
}
